﻿using System.Windows;
using System.Windows.Controls;
using System.Collections.Specialized;
using WP7UIKit.Common;
using System.Collections.Generic;
using System.Windows.Markup;
using System.Collections;
using System;
using System.Diagnostics;
using WP7UIKit.Model;
using System.Windows.Media;
using System.Windows.Data;
using System.Linq;
using System.Threading;
using Microsoft.Phone.Controls;
using System.Windows.Media.Animation;

namespace WP7UIKit.Controls
{
    [TemplatePart(Name = ListScrollerName, Type = typeof(ScrollViewer))]
    [TemplatePart(Name = ContainerStackName, Type = typeof(StackPanel))]
    [TemplatePart(Name = HeaderContentName, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = ItemsPresenterHostName,Type = typeof(Grid))]
    [TemplatePart(Name = ItemsPresenterName,Type = typeof(ItemsPresenter))]
    [TemplatePart(Name = TopFreshControlName,Type = typeof(TextBlock))]
    [TemplatePart(Name = BottomFreshControlName, Type = typeof(TextBlock))]
    public class WaterFallList : ItemsControl
    {
        #region ConstName
        private const string ListScrollerName = "ListScroller";
        private const string ContainerStackName = "ContainerStack";
        private const string HeaderContentName = "HeaderContent";
        private const string ListContaierName = "ListContaier";
        private const string ItemsPresenterName = "ItemsPresenter";
        private const string ItemsPresenterHostName = "ItemsPresenterHost";
        private const string TopFreshControlName = "TopFreshControl";
        private const string BottomFreshControlName = "BottomFreshControl";
        #endregion

        #region Fields
        private ScrollViewer _listScroller;
        private StackPanel _containerStack;
        private ContentPresenter _headerContent;
        private Grid _itemsPresenterHost;
        private ItemsPresenter _itemsPresenter;
        private Grid _itemsPanelGrid;
        private double[] _columnY;
        private GestureListener _gestureListener;//手势操作
        private TextBlock _topFreshControl;
        private TextBlock _bottomFreshControl;
        private double _oldVertical = 0.0;//上一次的偏移量
        private List<KeyValueInfo> _infos;
        #endregion

        #region Event
        public event VerticalScrollChangedEventHandler VerticalScrollChanged;   //滚动更新事件
        public event SelectionChangedEventHandler SelectionChanged;             //选择变化事件
        public event TapItemChangedEventHandler TapItemChanged;                 //Tap不同选项事件
        public event EventHandler PullDownToFetch;                              //下拉加载新数据事件
        public event EventHandler PullUpToFetch;                                //上拉加载旧数据事件
        #endregion

        #region Constructor
        public WaterFallList()
        {
            this.DefaultStyleKey = typeof(WaterFallList);
            SelectedIndex = -1;
            this.VerticalScrollChanged += new VerticalScrollChangedEventHandler(WaterFallList_VerticalScrollChanged);
        }

        void WaterFallList_VerticalScrollChanged(object sender, VerticalScrollChangedEventArgs e)
        {
            double verticalOffset = e.VerticallOffset;
            
            double monitorScreenHeight = _listScroller.ExtentHeight - verticalOffset;
            double screenHeight = _listScroller.ViewportHeight;
            //当动态区域小于可以区域高度的1.5倍并且是向下滑动时加载新的数据
            if (monitorScreenHeight <= screenHeight * 1.5 && _oldVertical - verticalOffset < 0)
            {
                ShowBottomFresh();
            }
            //当动态区域大于可见区域且滑动100(这个距离可以根据情况改变)像素时，再去重新布局控件
            if (monitorScreenHeight > screenHeight && Math.Abs(_oldVertical - verticalOffset) > 100)
            {
                foreach (object item in base.Items)
                {
                    WaterFallItem container = (WaterFallItem)GetItemContainerForObject(item);
                    double y = container.Margin.Top;
                    double vy = y - verticalOffset;
                    double headerHeight = _headerContent.ActualHeight;//要计算上头部的高度
                    if (vy < -300 - headerHeight || vy > 800 - headerHeight)
                    {
                        _itemsPanelGrid.Children.Remove(container);
                    }
                    else
                    {
                        if (!_itemsPanelGrid.Children.Contains(container))
                        {
                            _itemsPanelGrid.Children.Add(container);
                        }
                    }
                }
            }
            _oldVertical = verticalOffset;
            _itemsPanelGrid.Height = GetMaxHeight();//每次变化都要重置高度
        }
        #endregion

        #region DependencyProperties

        #region Header
        public object Header
        {
            get { return (object)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(WaterFallList), new PropertyMetadata(OnHeaderChanged));

        private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnHeaderChanged(e.OldValue, e.NewValue);
        }
        protected virtual void OnHeaderChanged(object oldHeader, object newHeader)
        {
        }
        #endregion

        #region HeaderTemplate
        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(WaterFallList), new PropertyMetadata(OnHeaderTemplateChanged));

        private static void OnHeaderTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnHeaderTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue);
        }

        protected virtual void OnHeaderTemplateChanged(DataTemplate oldHeaderTemplate, DataTemplate newHeaderTemplate)
        {
        }
        #endregion

        #region SelectedIndex

        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set { SetValue(SelectedIndexProperty, value); }
        }

        public static readonly DependencyProperty SelectedIndexProperty =
            DependencyProperty.Register("SelectedIndex", typeof(int), typeof(WaterFallList), new PropertyMetadata(OnSelectedIndexChanged));

        private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnSelectedIndexChanged(e.OldValue,e.NewValue);
        }

        protected virtual void OnSelectedIndexChanged(object oldValue, object newValue)
        {
            int oldIndex = (int)oldValue;
            int newIndex = (int)newValue;
            if (newIndex != -1)
            {
                SelectedIndex = newIndex;
                WaterFallItem container = (WaterFallItem)ItemContainerGenerator.ContainerFromIndex(newIndex);
                SelectedItem = ItemContainerGenerator.ItemFromContainer(container);

                IList removedItems = (null == oldValue) ? new object[0] : new object[] { oldValue };
                IList addedItems = (null == newValue) ? new object[0] : new object[] { newValue };
                if (this.SelectionChanged != null)
                {
                    this.SelectionChanged(this, new SelectionChangedEventArgs(removedItems, addedItems));
                }
            }
        }
        #endregion

        #region SelectedItem

        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(WaterFallList), new PropertyMetadata(OnSelectedItemChanged));

        protected static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnSelectedItemChanged(e.OldValue, e.NewValue);
        }

        protected virtual void OnSelectedItemChanged(object oldValue, object newValue)
        {
            if (newValue != oldValue)
            {
                if (newValue == null)
                {
                    SelectedIndex = -1;
                }
                else
                {
                    WaterFallItem container = (WaterFallItem)ItemContainerGenerator.ContainerFromItem(newValue);
                    int selectedIndex = ItemContainerGenerator.IndexFromContainer(container);
                    SelectedIndex = selectedIndex;
                    SelectedItem = ItemContainerGenerator.ItemFromContainer(container);
                }
            }
        }
        #endregion

        #region ColumnCount
        public int ColumnCount
        {
            get { return (int)GetValue(ColumnCountProperty); }
            set { SetValue(ColumnCountProperty, value); }
        }

        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.Register("ColumnCount", typeof(int), typeof(WaterFallList), new PropertyMetadata(OnColumnCountChanged));

        private static void OnColumnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnColumnCountChanged((int)e.OldValue,(int) e.NewValue);
        }

        protected virtual void OnColumnCountChanged(int oldCloumnCount, int newColumnCount)
        {
        }
        #endregion

        #region ColumnMargin
        public double ColumnMargin
        {
            get { return (double)GetValue(ColumnMarginProperty); }
            set { SetValue(ColumnMarginProperty, value); }
        }

        public static readonly DependencyProperty ColumnMarginProperty =
            DependencyProperty.Register("ColumnMargin", typeof(double), typeof(WaterFallList), new PropertyMetadata(OnColumnMarginChanged));

        private static void OnColumnMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnColumnMarginChanged((double)e.OldValue, (double)e.NewValue);
        }

        protected virtual void OnColumnMarginChanged(double oldMargin, double newMargin)
        {
            
        }
        #endregion

        #endregion

        #region Override

        #region ApplyTemplate
        
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //初始化控件
            _listScroller = (ScrollViewer)GetTemplateChild(ListScrollerName);
            _containerStack = (StackPanel)GetTemplateChild(ContainerStackName);
            _headerContent = (ContentPresenter)GetTemplateChild(HeaderContentName);
            _itemsPresenterHost = (Grid)GetTemplateChild(ItemsPresenterHostName);
            _itemsPresenter = (ItemsPresenter)GetTemplateChild(ItemsPresenterName);
            _topFreshControl = (TextBlock)GetTemplateChild(TopFreshControlName);
            _bottomFreshControl = (TextBlock)GetTemplateChild(BottomFreshControlName);
            _itemsPresenter.LayoutUpdated += new EventHandler(_itemsPresenter_LayoutUpdated);
            _columnY = new double[ColumnCount];
            //注册事件
            this.SetBinding(
                              DependencyProperty.RegisterAttached("VerticalOffset",
                                  typeof(double),
                                  this.GetType(),
                                  new PropertyMetadata((d, e) =>
                                  {
                                      if (VerticalScrollChanged != null)
                                          VerticalScrollChanged(_listScroller, new VerticalScrollChangedEventArgs(_listScroller.VerticalOffset));
                                  })),
                              new Binding("VerticalOffset") { Source = this._listScroller });
            _gestureListener = GestureService.GetGestureListener(_listScroller);
            _gestureListener.DragDelta += _gestureListener_DragDelta;
            _gestureListener.DragCompleted += _gestureListener_DragCompleted;
        }
        #endregion

        #region DataItem-UIItem
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is WaterFallItem;
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new WaterFallItem();
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            WaterFallItem container = (WaterFallItem)element;
            container.VerticalAlignment = VerticalAlignment.Top;
            container.HorizontalAlignment = HorizontalAlignment.Left;
            container.Tap += container_Tap;
            this.Infos.Add(new KeyValueInfo(item,container));
        }

        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            
            base.ClearContainerForItemOverride(element, item);
            KeyValueInfo removeInfo =Infos.Where(x => x.Key == item).FirstOrDefault();
            int removeIndex = Infos.IndexOf(removeInfo);                                //删除的坐标
            _columnY = new double[ColumnCount];                                         //重置坐标数组
            ResetElementPosition(removeIndex);                                          //重置元素并设置动画效果
            Infos.Remove(removeInfo);                                                   //删除infos中的该元素
        }

        private Storyboard CreateRemoveStoryBoard(WaterFallItem container,Thickness oldThickness,Thickness newThickess)
        {
            Storyboard storyboard = new Storyboard();
            Timeline line = ThicknessAnimation.Create(container, ContentControl.MarginProperty, TimeSpan.FromMilliseconds(1000), oldThickness, newThickess);
            storyboard.Children.Add(line);
            return storyboard;
        }

        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
        }
        #endregion
        
        #endregion

        #region Delegate
        //重新进行图片墙布局
        void _itemsPresenter_LayoutUpdated(object sender, EventArgs e)
        {
            _itemsPresenter.LayoutUpdated -= new EventHandler(_itemsPresenter_LayoutUpdated);
            _itemsPanelGrid = (Grid)VisualTreeHelper.GetChild(_itemsPresenter, 0);
            //给元素设置位置
            SetElementPosition();
        }

        void _gestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
        {
            HideTopFresh();
        }

        void _gestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
        {
            if (e.VerticalChange > 30 && _listScroller.VerticalOffset == 0.0)
            {
                this.ShowTopFresh();
            }
        }

        void container_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            WaterFallItem container = (WaterFallItem)sender;
            int index = ItemContainerGenerator.IndexFromContainer(container);
            object item = ItemContainerGenerator.ItemFromContainer(container);
            this.SelectedIndex = index;
            if (this.TapItemChanged != null)
            {
                this.TapItemChanged(container, new TapEventArgs(index, item));//TODO 自定义选中时的动画效果
            }
        }
        #endregion

        #region MestureLayoutMethod
        //从某个索引开始重设元素的位置
        private void ResetElementPosition(int index)
        {
            for (int i = 0; i < Infos.Count; i++)
            {
                KeyValueInfo info = Infos.ElementAt(i);
                object item = info.Key;
                WaterFallItem container = (WaterFallItem)info.Value;
                int lowestIndex = GetLowestIndex();
                double width = container.ActualWidth;
                double height = container.ActualHeight;
                double x = (ColumnMargin + width) * lowestIndex + ColumnMargin;
                double y = _columnY[lowestIndex] + ColumnMargin;
                _columnY[lowestIndex] = y + height;
                container.Margin = new Thickness(x, y, 0, 0);
            }
        }

        private void SetElementPosition()
        {
            foreach (KeyValueInfo info in Infos) 
            {
                object item = info.Key;
                WaterFallItem container = (WaterFallItem)info.Value;
                int lowestIndex = GetLowestIndex();
                double width = container.ActualWidth;
                double height = container.ActualHeight;
                double x = (ColumnMargin + width) * lowestIndex + ColumnMargin;
                double y = _columnY[lowestIndex] + ColumnMargin;
                _columnY[lowestIndex] = y + height;
                container.Margin = new Thickness(x, y, 0, 0);
            }
        }

        protected virtual int GetLowestIndex()
        {
            int index = 0;
            for (int i = 1; i < ColumnCount; i++)
            {
                if (_columnY[index] > _columnY[i])
                {
                    index = i;
                }
            }
            return index;
        }

        /// <summary>
        /// 最高的列的高度
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public double GetMaxHeight()
        {
            int index = 0;
            for (int i = 1; i < ColumnCount; i++)
            {
                if (_columnY[index] < _columnY[i])
                {
                    index = i;
                }
            }
            return _columnY[index];
        }

        private void ShowTopFresh()
        {
            if (_topFreshControl != null && _topFreshControl.Visibility == Visibility.Collapsed)
            {
                _topFreshControl.Visibility = Visibility.Visible;
            }
        }

        private void HideTopFresh()
        {
            if (_topFreshControl != null && _topFreshControl.Visibility == Visibility.Visible)
            {
                _topFreshControl.Visibility = Visibility.Collapsed;
                if (PullDownToFetch != null)
                {
                    this.PullDownToFetch(this, EventArgs.Empty);
                }
            }
        }

        private void ShowBottomFresh()
        {
            if (_bottomFreshControl != null && _bottomFreshControl.Visibility == Visibility.Collapsed)
            {
                _bottomFreshControl.Visibility = Visibility.Visible;
                if (this.PullUpToFetch != null)
                {
                    this.PullUpToFetch(this, EventArgs.Empty);
                }
            }
        }

        private void HideBottomFresh()
        {
            if (_bottomFreshControl != null && _bottomFreshControl.Visibility == Visibility.Visible)
            {
                _bottomFreshControl.Visibility = Visibility.Collapsed;
            }
        }
        #endregion

        #region ItemContainerMethod
        private object GetItemContainerForObject(object item)
        {
            return Infos.Where(x => x.Key == item).FirstOrDefault().Value;
        }

        protected List<KeyValueInfo> Infos
        {
            get
            {
                if (this._infos == null)
                {
                    this._infos = new List<KeyValueInfo>();
                }
                return _infos;
            }
        }
        #endregion
    }
}
